package chapter7;

import java.util.stream.LongStream;
import java.util.stream.Stream;

public class ParallelStreams {
    /**
     * 顺序求和
     * @param n
     * @return
     */

    public static long sequentialSum(long n){
        return Stream.iterate(1L, i -> i + 1)  //生成自然数无限流
                .limit(n)
                .reduce(0L, Long::sum); //对所有数字求和来归纳流
    }


    /**
     * 迭代求和
     * @param n
     * @return
     */
    public static long iterativeSum(long n){
        long result = 0;
        for (long i = 1L; i <= n; i++){
            result += i;
        }
        return result;
    }

    /**
     * 并行求和
     * @param n
     * @return
     */
    public static long parallelSum(long n){
        return Stream.iterate(1L, i -> i + 1)
                .limit(n)
                .parallel()
                .reduce(0L, Long::sum);
    }

    public static long rangeSum(long n){
        return LongStream.rangeClosed(1, n)
                .parallel()
                .reduce(0L, Long::sum);
    }

    /**
     * 正确使用并行流
     *      错用并行流而产生错误的首要原因，就是使用的算法改变了某些共享状态
     *
     *      这种代码实现方式很普遍。初始化一个累加器 一个个遍历列表中的元素 把它们和累加器相加
     *      但这种代码有很大的问题，因为它本质上就是顺序的，每次访问total都会出现数据竞争 如果尝试使用同步来修复 就完全失去了并行的意义
     *
     *      测试结果显示 每次结算出的结果都不一致。 这是由于多个线程同时访问累计器 执行total += value 它不是一个原子操作
     *      问题的根源在于  forEach中调用的方法有副作用，它会改变多个线程共享的对象的可变状态  要是想要并行Stream又不想引发类似的意外  就必须避免这种情况
     */
    public static long sideEffectSum(long n){
        Accumulator accumulator = new Accumulator();
//        LongStream.rangeClosed(1, n).forEach(accumulator::add);
        LongStream.rangeClosed(1, n).parallel().forEach(accumulator::add);
        return accumulator.total;
    }
}
